#include "BondCalendar.h"

#include <core/time/date.h>
#include <core/time/time.h>
#include <uam/ServerTimeMgr.h>

#include <QDate>

#include "BondContainer.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
// #define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// class CBondCalendar

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CBondCalendar::CBondCalendar() {}

CBondCalendar::~CBondCalendar() { Clear(); }

CBondCalendar& CBondCalendar::instance() {
  static CBondCalendar s_bc;
  return s_bc;
}

void CBondCalendar::Clear() {
  m_holiday.date.clear();
  return;
}

void CBondCalendar::Add(int nDate, BondHoliday& element) {
  if (nDate <= 0) return;
  std::map<int, BondHolidayList>::iterator it = m_holiday.date.find(nDate);
  if (it == m_holiday.date.end()) {
    BondHolidayList list;
    list.holidays.push_back(element);
    m_holiday.date.emplace(std::make_pair(nDate, list));
  } else {
    // [内存优化][scofined.qi]
    // 确保vector中没有浪费的空间,每多一个就浪费53字节
    std::vector<BondHoliday>& holidays = it->second.holidays;
    if (holidays.size() >= holidays.capacity())
      holidays.reserve(holidays.capacity() + 1);
    holidays.push_back(element);
  }
  // m_holiday.m_data.push_back( element);
  // int nIndex = m_holiday.m_data.size()-1;
  return;  // nIndex;
}

int CBondCalendar::GetSize() { return m_holiday.date.size(); }

bool CBondCalendar::GetHolidayByDate(const std::string& szCountry,
                                     const std::string& szMarket, int nDate) {
  int year = nDate / 10000;
  int month = (nDate / 100) % 100;
  int day = nDate % 100;

  QDate date(year, month, day);

  std::map<int, BondHolidayList>::iterator it;
  it = m_holiday.date.find(nDate);
  if (it != m_holiday.date.end()) {
    std::vector<BondHoliday>::iterator it2;
    for (it2 = it->second.holidays.begin(); it2 != it->second.holidays.end();
         it2++) {
      BondHoliday& bh = *it2;
      if (strcmp(bh.country, szCountry.c_str()) == 0 &&
          strcmp(bh.market, szMarket.c_str()) == 0) {
        if (strcmp(bh.status, "H") == 0)
          return true;
        else if (date.dayOfWeek() >= 6)
          return false;
      }
    }
  }

  if (date.dayOfWeek() >= 6) return true;

  return false;
}

void CBondCalendar::CalcHolidays(const std::string sMarket, time_t tBegin,
                                 time_t tEnd, BondHolidays_& bhd_) {
  int nBegin = TimeToInt(tBegin);
  int nEnd = TimeToInt(tEnd);

  bhd_.nNaturalDays = DiffDays(nBegin, nEnd);

  std::string sCountry("CNY");

  int nHolidays = 0;
  for (int nDay = 0; nDay < bhd_.nNaturalDays; ++nDay) {
    if (IsHoliday(sCountry, sMarket, nBegin)) nHolidays++;

    nBegin = IncreaseOneDay(nBegin);
  }

  bhd_.nWorkingDays = bhd_.nNaturalDays - nHolidays;
}

bool CBondCalendar::GetBondHolidayById(const std::string& szMarket, int nDate,
                                       BondHoliday& stHoliday) {
  std::map<int, BondHolidayList>::iterator it;
  it = m_holiday.date.find(nDate);
  if (it != m_holiday.date.end()) {
    std::vector<BondHoliday>::iterator it2;
    for (it2 = it->second.holidays.begin(); it2 != it->second.holidays.end();
         it2++) {
      if (std::string(it2->market) == std::string(szMarket)) {
        stHoliday = *it2;
        return true;
      }
    }
  }
  return false;
}

CBondHoliday& CBondCalendar::GetBondHoliday() { return m_holiday; }

bool CBondCalendar::IsHoliday_CHY(const CBondInfo& bondInfo, bool& isCIB,
                                  int& nDayCIB, int& nDaySSE) {
  return IsHoliday_CHY_Time(bondInfo, ServerTimeMgr::instance().serverTime(),
                            isCIB, nDayCIB, nDaySSE);
}

bool CBondCalendar::IsHoliday_CHY_Time(const CBondInfo& bondInfo,
                                       const time_t tNow, bool& isCIB,
                                       int& nDayCIB, int& nDaySSE) {
  struct tm* tt = localtime(&tNow);

  int nNow =
      (tt->tm_year + 1900) * 10000 + (tt->tm_mon + 1) * 100 + tt->tm_mday;

  int nMautrityDate = atoi(bondInfo.GetMaturityDate());

  if (nNow > nMautrityDate) return false;  // 已过期
  // if (nNow / 10000 < nMautrityDate / 10000 - 2)return
  // false;//大于第二年12.31日

  nDayCIB = 0;
  nDaySSE = 0;

  bool breakCIB = false;
  bool breakSSE = false;
  bool bFirst = true;
  static std::string CNY = "CNY";
  static std::string CIB = "CIB";
  static std::string SSE = "SSE";
  while (1) {
    bool bIsValid = false;
    if (GetHolidayByDate(CNY, CIB, nMautrityDate)) {
      if (!breakCIB && (bFirst || nDayCIB > 0)) nDayCIB++;

      bIsValid = true;
    } else {
      breakCIB = true;
    }
    if (GetHolidayByDate(CNY, SSE, nMautrityDate)) {
      if (!breakSSE && (bFirst || nDaySSE > 0)) nDaySSE++;

      bIsValid = true;
    } else {
      breakSSE = true;
    }

    bFirst = false;

    if (!bIsValid) break;
    IncreaseOneDay(nMautrityDate);
  }

  if (nDayCIB <= 0 && nDaySSE <= 0) return false;

  isCIB = true;
  if (nDayCIB == 0) isCIB = false;

  return true;
}

int CBondCalendar::IncreaseOneDay(int& nDate) {
  time_t tTime = IntToTime(nDate);
  tTime += 24 * 60 * 60;
  nDate = TimeToInt(tTime);
  return nDate;
}

int CBondCalendar::ReduceOneDay(int& nDate) {
  time_t tTime = IntToTime(nDate);
  tTime -= 24 * 60 * 60;
  nDate = TimeToInt(tTime);
  return nDate;
}

bool CBondCalendar::IsHoliday_CHY_Primary(const CBondInfo& bondInfo,
                                          const time_t tNow, bool& isCIB,
                                          int& nDayCIB, int& nDaySSE) {
  return IsHoliday_CHY_Time(bondInfo, tNow, isCIB, nDayCIB, nDaySSE);
}

time_t CBondCalendar::IntToTime(int nDate) {
  struct tm tt;
  memset(&tt, 0, sizeof(tm));
  tt.tm_year = nDate / 10000 - 1900;
  tt.tm_mon = (nDate / 100) % 100 - 1;
  tt.tm_mday = nDate % 100;
  tt.tm_hour = 8;
  time_t t = mktime(&tt);

  return t;
}

int CBondCalendar::TimeToInt(time_t tTime) {
  return qb::base::IntDate::timeToInt(tTime);
}

bool CBondCalendar::IsHoliday(const std::string& szCountry,
                              const std::string& szMarket, int nDate) {
  return GetHolidayByDate(szCountry, szMarket, nDate);
}

bool CBondCalendar::IsHoliday(const std::string& szCountry,
                              const std::string& szMarket, time_t tTime) {
  return IsHoliday(szCountry, szMarket, TimeToInt(tTime));
}

int CBondCalendar::DiffDays(int nStartDate, int nEndDate) {
  time_t tStartTime = IntToTime(nStartDate);
  time_t tEndTime = IntToTime(nEndDate);

  time_t nDiffTime = tEndTime - tStartTime;

  return nDiffTime / 24 / 3600;
}

int CBondCalendar::IncreaseDay(int& nDate, int nDays) {
  time_t tTime = IntToTime(nDate);
  tTime += 24 * 60 * 60 * nDays;
  nDate = TimeToInt(tTime);
  return nDate;
}

void CBondCalendar::DumpStatus(int mode) const {}

void CBondCalendar::GetNextTradingDay(time_t& t, const char* cCountry,
                                      const char* cMarket, bool bWeekend) {
  if (strlen(cMarket) <= 0 || strlen(cCountry) <= 0) return;

  while (1) {
    t += 60 * 60 * 24;
    tm tmDay = qb::base::time2tm(t);

    int nDate = (tmDay.tm_year + 1900) * 10000 + (tmDay.tm_mon + 1) * 100 +
                tmDay.tm_mday;
    if (!CBondCalendar::instance().GetHolidayByDate(cCountry, cMarket, nDate)) {
      if (bWeekend) {
        if (tmDay.tm_wday == 0 || tmDay.tm_wday == 6) continue;
      }
      t = IntToTime(nDate);
      break;
    }
  }
}

void CBondCalendar::GetNextTradingDay(time_t& t, bool bWeekend /*=false*/) {
  GetNextTradingDay(t, "CNY", "CIB", bWeekend);
}

void CBondCalendar::GetPreTradingDay(time_t& t, const char* cCountry,
                                     const char* cMarket,
                                     bool bWeekend /*= false*/) {
  if (strlen(cMarket) <= 0 || strlen(cCountry) <= 0) return;

  while (1) {
    t -= 60 * 60 * 24;
    tm tmDay = qb::base::time2tm(t);
    int nDate = (tmDay.tm_year + 1900) * 10000 + (tmDay.tm_mon + 1) * 100 +
                tmDay.tm_mday;
    if (!CBondCalendar::instance().GetHolidayByDate(cCountry, cMarket, nDate)) {
      if (bWeekend) {
        if (tmDay.tm_wday == 0 || tmDay.tm_wday == 6) continue;
      }
      t = IntToTime(nDate);
      break;
    }
  }
}

void CBondCalendar::GetPreTradingDay(time_t& t, bool bWeekend /*=false*/) {
  GetPreTradingDay(t, "CNY", "CIB", bWeekend);
}